package com.tianji.learning.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.IPointsBoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements
        IPointsBoardService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserClient userClient;
    @Autowired
    private ILearningLessonService learningLessonService;

    //1. 根据userId和courseId从learning_lesson查询课表
    //2. 根据lesson_id从learning_record查询学习记录合集
    //3. 封装LearningLessDTO
    //4. 返回结果
    @Override
    public PointsBoardVO queryPointsBoardBySeason(PointsBoardQuery query) {
        Long season = query.getSeason();//赛季id
        PointsBoard pointsBoard=null;//我的排名
        List<PointsBoard> pointsBoardList=null;//集合排名
        //1. 判断查询的是当前还是历史赛季
        if(season == null || season==0){//2. 查询当前赛季
            //组装key
            String yyyyMM = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
            String key="boards"+yyyyMM;//board:202308
            //2-1 查询当前赛季我的积分和排名
            pointsBoard=queryMyCurrentBoard(key);
            //2-2 查询当前赛季的排行榜 第一页【0,9】 第二页【10，19】
            int start = (query.getPageNo() - 1) * query.getPageSize();
            int end = start + query.getPageSize() - 1;
            pointsBoardList=queryCurrentBoardList(key,start,end);
        }else { //查询历史赛季 todo

        }
        //4.封装PointsBoardVO
        PointsBoardVO vo = new PointsBoardVO();
        if(pointsBoard != null){ // 封装我的
            vo.setPoints(pointsBoard.getPoints());
            vo.setRank(pointsBoard.getRank());
        }
        if(CollUtil.isNotEmpty(pointsBoardList)){ //分组列表
            //批量查询用户信息
            Set<Long> userIds = pointsBoardList.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
            List<UserDTO> userDTOList = userClient.queryUserByIds(userIds);
            Map<Long, UserDTO> userDTOMap = userDTOList.stream().collect(Collectors.toMap(UserDTO::getId, userDTO -> userDTO));
            ArrayList<PointsBoardItemVO> itemVoList = new ArrayList<>(pointsBoardList.size());
            for (PointsBoard board : pointsBoardList) {
                PointsBoardItemVO itemVO = new PointsBoardItemVO();
                itemVO.setPoints(board.getPoints());
                itemVO.setRank(board.getRank());
                itemVO.setName(userDTOMap.get(board.getUserId()).getName());
                itemVoList.add(itemVO);
            }
            vo.setBoardList(itemVoList);

        }
        // 5. 返回结果
        return vo;
    }

    //创建历史榜单表
    @Override
    public void createTable() {
        //1. 获取上月时间
        LocalDate lastDate = LocalDate.now().minusMonths(1);
        String lastDateSeason = lastDate.format(DateTimeFormatter.ofPattern("yyyyMM"));
        //2. 创建表
        getBaseMapper().createTable("points_board_"+lastDateSeason);


    }

    //历史榜单数据持久化
    @Override
    public void savePointBoard2DB() {
        //1. 获取上月时间
        LocalDate lastDate = LocalDate.now().minusMonths(1);
        String lastDateSeason = lastDate.format(DateTimeFormatter.ofPattern("yyyyMM"));
        //2. 准备分页数据
        Integer pageNo=1;
        Integer pageSize=1000;
        //3. while遍历读书并同步数据
        while (true){
            //计算start、end
            Integer start=(pageNo-1)*pageSize;
            Integer end=pageNo*pageSize-1;
            // 分页读取redis中的数据
            String key="boards:"+lastDateSeason;
            List<PointsBoard> pointsBoardList = queryCurrentBoardList(key, start, end);
            // pointsBoardList为空表示redis读完了
            if(CollUtil.isEmpty(pointsBoardList)){
                break;
            }
            //名词作为主键
            pointsBoardList = pointsBoardList.stream().map(p -> {
                p.setId(p.getRank().longValue());
                return p;
            }).collect(Collectors.toList());
            //保存数据库
            getBaseMapper().saveBySeason("points_board_"+lastDateSeason,pointsBoardList);
            //当前页++
            pageNo++;
        }
    }

    //查询当前赛季的排行榜
    private List<PointsBoard> queryCurrentBoardList(String key, int start, int end) {
        //查询指定页的数据 zrevrange key start stop WITHSCORES
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        if(CollUtil.isEmpty(typedTuples)){
            return CollUtil.newArrayList();
        }
        //封装返回结果 在redis中rank是从0开始的
        int rank=start+1;
        ArrayList<PointsBoard> list = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            String userId = typedTuple.getValue();
            Double points = typedTuple.getScore();
            PointsBoard pointsBoard = new PointsBoard();
            pointsBoard.setPoints(points.intValue());//分数
            pointsBoard.setUserId(Long.valueOf(userId));//userId
            pointsBoard.setRank(rank++);
            list.add(pointsBoard);
        }
        return list;
    }

    // 查询当前赛季我的积分和排名
    private PointsBoard queryMyCurrentBoard(String key) {
        //查询我的积分
        Double score = stringRedisTemplate.opsForZSet().score(key, UserContext.getUser().toString());
        //查询我的排名 从0开始
        Long rank = stringRedisTemplate.opsForZSet().reverseRank(key, UserContext.getUser().toString());
        //封装PointsBoard
        PointsBoard pointsBoard = new PointsBoard();
        pointsBoard.setPoints(score==null?0:score.intValue());
        pointsBoard.setRank(rank==null?0:rank.intValue()+1);
        return pointsBoard;
    }
}
